package com.jprocms.module.cms.service.sitetpl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.ftp.Ftp;
import com.jpro.framework.common.exception.ServerException;
import com.jpro.module.infra.framework.file.core.client.ftp.FtpFileClientConfig;
import com.alibaba.fastjson.annotation.JSONField;
import com.jprocms.module.cms.framework.file.UploadUtils;
import com.jprocms.module.cms.framework.file.core.client.ftp.FtpFileClient;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.SocketException;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static com.jprocms.module.cms.constant.CmsSysConstants.SPT;
import static org.apache.commons.net.ftp.FTPFile.DIRECTORY_TYPE;

/**
 * @author: zrcms
 */
public class FtpResFile implements IResFile {
    private static final Logger log = LoggerFactory.getLogger(FtpResFile.class);

    public static void main(String[] args) {
        System.out.println(FilenameUtils.getFullPath("/tpl/1111/default"));
    }

    /**
     * 获取文件 （空文件夹没有文件的则不会创建）
     *
     * @param fileName  获取文件或文件夹名称相对站点目录
     * @param root      站点根目录
     * @param localPath 本地保存文件目录
     * @return
     * @throws ServerException
     */
    public boolean retrieveFile(String fileName, String root, String localPath) throws ServerException {
        boolean flag = false;
        OutputStream os = null;
        try {
            Ftp clientFtp = ftpFileClient.getFtp();
            FTPClient ftp = clientFtp.getClient();
            if (ftp != null) {
                clientFtp.reconnectIfTimeout();
                String filename = basePath + root + SPT + fileName;
                if (clientFtp.getClient().changeWorkingDirectory(filename)) {
                    FTPFile[] fs = clientFtp.getClient().listFiles();
                    for (FTPFile ff : fs) {
                        String relativeFilename = fileName + SPT + ff.getName();
                        //目录
                        if (ff.getType() == DIRECTORY_TYPE) {
//                            retrieveFile(fileName + SPT + ff.getName(), path,localPath);
                            retrieveFile(relativeFilename, root, localPath);
                        } else {
                            File localFile = new File(localPath + SPT + relativeFilename);
                            File parent = FileUtil.getParent(localFile, 1);
                            if (!parent.exists()) {
                                parent.mkdirs();
                            }
                            os = new FileOutputStream(localFile);
                            flag = clientFtp.getClient().retrieveFile(ff.getName(), os);
                        }
                    }
                }
            }
        } catch (IOException e) {
            log.error("ftp store error", e);
            flag = false;
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return flag;
    }

    /**
     * 阅读Ftp服务器文件
     *
     * @param fileName 文件名
     * @return 字符串
     * @throws IOException IO异常
     */
    public String readFile(String fileName) throws IOException, ServerException {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }
        return IoUtil.read(IoUtil.toStream(ftpFileClient.getContent(fileName))).toString();
    }

    public FTPFile get(String pathname) throws IOException, ServerException {
        Ftp ftp = ftpFileClient.getFtp();
        ftp.reconnectIfTimeout();
        FTPClient ftpClient = ftp.getClient();
        FTPFile file = null;
        String fileName = basePath + pathname;
        String name = FilenameUtils.getName(fileName);
        String path = FilenameUtils.getFullPathNoEndSeparator(fileName);
        //取路径  第一次确定当前是否是文件夹  第二次是退回到上层目录 取目标文件夹
        if (ftpClient.changeWorkingDirectory(fileName)) {
            FTPFile[] ff = ftpClient.listDirectories(path);
            if (ff != null && ff.length > 0) {
                file = ff[0];
            }
        } else if (StringUtils.isBlank(path) || ftpClient.changeWorkingDirectory(path)) {
            //取文件
            FTPFile[] ff = ftpClient.listFiles(name);
            if (ff != null && ff.length > 0) {
                file = ff[0];
            }
        }
        if (file != null) {
            file.setName(FilenameUtils.getName(file.getName()));
        }
        return file;
    }

    public FTPFile[] list(String path) {
        Ftp ftp = ftpFileClient.getFtp();
        path = basePath + path;
        path = path.replace("//", "/");
        FTPFile[] list;
        try {
            ftp.reconnectIfTimeout();
            list = ftp.getClient().listFiles(path);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    public File retrieve(String name, String fileName) throws IOException, ServerException {
        String path = System.getProperty("java.io.tmpdir");
        File file = new File(path, fileName);
        file = UploadUtils.getUniqueFile(file);
        FTPClient ftp = ftpFileClient.getFtp().getClient();
        OutputStream output = new FileOutputStream(file);
        ftp.retrieveFile(basePath + name, output);
        output.close();
        ftp.logout();
        ftp.disconnect();
        return file;
    }

    public boolean deleteFile(String fileName) throws IOException, ServerException {
        String fileUrl = fileName;
        if (StringUtils.isNotBlank(basePath) && !SPT.equals(basePath)) {
            fileUrl = basePath + SPT + fileName;
            fileUrl = fileUrl.replace("//", "/");
        }
        Ftp fileClientFtp = ftpFileClient.getFtp();
        FTPClient ftp = fileClientFtp.getClient();
        FTPFile f = get(fileName);
        boolean succ;
        if (f.getType() == DIRECTORY_TYPE) {
            fileClientFtp.reconnectIfTimeout();
            FTPFile[] ftpFiles = list(fileUrl);
            if (ftpFiles != null && ftpFiles.length > 0) {
                for (FTPFile file : ftpFiles) {
                    delete(file, ftp, fileUrl);
                }
            }
            fileClientFtp.reconnectIfTimeout();
            succ = fileClientFtp.delDir(fileUrl);
        } else {
            fileClientFtp.reconnectIfTimeout();
            succ = fileClientFtp.delFile(fileUrl);
        }
        return succ;
    }

    /**
     * 递归删除文件
     *
     * @param file     文件
     * @param ftp      {@link FTPClient}
     * @param fileName 上级目录
     * @throws IOException IO异常
     */
    private void delete(FTPFile file, FTPClient ftp, String fileName) throws IOException {
        fileName += SPT + file.getName();
        //解决中文乱码问题
        if (file.getType() == 1) {
            FTPFile[] ftpFiles = ftp.listFiles(fileName);
            if (ftpFiles != null && ftpFiles.length > 0) {
                for (FTPFile ftpFile1 : ftpFiles) {
                    delete(ftpFile1, ftp, fileName);
                }
            }
            ftp.removeDirectory(fileName);
        } else {
            ftp.deleteFile(fileName);
        }
    }

    public boolean restore(String name, File file) throws IOException, ServerException {
        store(name, FileUtils.openInputStream(file));
        file.deleteOnExit();
        return true;
    }


    /**
     * 添加文件
     *
     * @param filename 文件名
     * @param in       输入流
     * @return 文件名
     */
    public int storeByFilename(String filename, InputStream in) throws ServerException {
        return store(filename, in);
    }

    private int store(String remote, InputStream in) throws ServerException {
        try {
            Ftp ftp = ftpFileClient.getFtp();
            ftp.reconnectIfTimeout();
            FTPClient ftpClient = ftp.getClient();
            String filename = basePath + remote;
            String fullPath = FilenameUtils.getFullPath(filename);
            if (!ftpClient.changeWorkingDirectory(fullPath)) {
                String[] ps = StringUtils.split(fullPath, SPT);
                String p = SPT;
                ftpClient.changeWorkingDirectory(p);
                for (String s : ps) {
                    p += s + SPT;
                    if (!ftpClient.changeWorkingDirectory(p)) {
                        ftpClient.makeDirectory(s);
                        ftpClient.changeWorkingDirectory(p);
                    }
                }
            }
            ftpClient.storeFile(FilenameUtils.getName(filename), in);
            ftpClient.logout();
            ftpClient.disconnect();
            return 0;
        } catch (SocketException e) {
            log.error("ftp store error", e);
            return 3;
        } catch (IOException e) {
            log.error("ftp store error", e);
            return 4;
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
    }

    public FtpResFile(FTPFile ftpFile, String path, FtpFileClient ftpFileClient, String basePath) {
        this.ftpFile = ftpFile;
        this.ftpFileClient = ftpFileClient;
        this.basePath = basePath;
        this.path = path;
    }

    public FtpResFile(FTPFile ftpFile, String path, FtpFileClient ftpFileClient, String basePath, Integer parentId) {
        this.ftpFile = ftpFile;
        this.ftpFileClient = ftpFileClient;
        this.basePath = basePath;
        this.path = path;
        this.parentId = parentId;
    }

    public FtpResFile(FtpFileClient ftpFileClient, String basePath) {
        this.ftpFileClient = ftpFileClient;
        this.basePath = basePath;
    }

    private FTPFile ftpFile;
    private FtpFileClient ftpFileClient;
    private FtpFileClientConfig ftpFileClientConfig;

    private String basePath;

    private String path;

    private List<IResFile> children;
    //虚拟属性 父id，根节点0
    private Integer parentId = 0;

    /**
     * 是否包含页面
     */
    private Boolean includePage;

    private Integer id;

    /**
     * 文件名
     */
    private String fileName;
    /**
     * 文件路径
     */
    private String name;
    /**
     * 文件大小
     */
    private long size;

    private String sizeUnit;
    /**
     * 文件所属路径
     */
    private String root;

    /**
     * 最后修改时间
     */
    private long lastModified;
    /**
     * 最后修改时间
     */
    private LocalDateTime lastModifiedDate;

    /**
     * 是否文件夹
     */
    private boolean directory;

    public FtpResFile() {
        super();
    }

    @Override
    public List<IResFile> getChildren() {
        return children;
    }

    public List<IResFile> getAllChildren() {
        if (this.children != null) {
            return this.children;
        }
        List<IResFile> list = new ArrayList<>();
        getChildForFile(this, list);
        return list;
    }

    private List<IResFile> getChildForFile(FtpResFile resFile, List<IResFile> container) {
        FTPFile[] ftpFiles = list(resFile.getName());
        if (ftpFiles != null) {
            for (FTPFile ftpFile : ftpFiles) {
                if (!ftpFile.isFile()) {
                    //resFile.getName() 上级目录的文件夹名称
                    FtpResFile fw = new FtpResFile(ftpFile, resFile.getName(), ftpFileClient, basePath, resFile.getParentId());
                    //自己的ID设置下级的父层
                    fw.setParentId(resFile.getId());
                    fw.setId(RandomUtil.getSecureRandom().nextInt(Integer.MAX_VALUE));
                    container.add(fw);
                    getChildForFile(fw, container);
                }
            }
        }
        return container;
    }

    @Override
    public void setChildren(List<IResFile> resFiles) {
        this.children = resFiles;
    }

    @Override
    public String getName() {
        return (StringUtils.isNotBlank(getRoot()) ? getRoot() : "") + SPT + getFilename();
    }

    @Override
    public String getRoot() {
        if (path != null && path.lastIndexOf(SPT) == path.length() - 1) {
            path = path.substring(0, path.length() - 1);
        }
        return path;
    }

    @Override
    public String getFilename() {
        return ftpFile != null ? ftpFile.getName() : null;
    }

    @Override
    public String getSource() throws ServerException {
        if (isDirectory()) {
            return null;
        } else {
            try {
                return readFile(getName());
            } catch (FileNotFoundException e) {
                return null;
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return null;
    }

    @Override
    public long getLastModified() {
        return ftpFile.getTimestamp().getTimeInMillis();
    }

    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    @Override
    public LocalDateTime getLastModifiedDate() {
        return LocalDateTimeUtil.of(ftpFile.getTimestamp().getTime());
    }

    @Override
    public long getLength() {
        return ftpFile.getSize();
    }

    @Override
    public int getSize() {
        return (int) (getLength() / 1024) + 1;
    }

    @Override
    public String getSizeUnit() {
        //定义GB的计算常量
        int gb = 1024 * 1024;
        //定义MB的计算常量
        int mb = 1024;
        //格式化小数
        DecimalFormat df = new DecimalFormat("0.00");
        String resultSize;
        if (getSize() / gb >= 1) {
            //如果当前Byte的值大于等于1GB
            resultSize = df.format(getSize() / (float) gb) + "GB";
        } else if (getSize() / mb >= 1) {
            //如果当前Byte的值大于等于1MB
            resultSize = df.format(getSize() / (float) mb) + "MB";
        } else {
            resultSize = getSize() + "KB";
        }
        sizeUnit = resultSize;
        return sizeUnit;
    }

    /**
     * 获取FTP文件
     *
     * @return FTPFile
     */
    public FTPFile getFtpFile() {
        return this.ftpFile;
    }

    @Override
    public boolean isDirectory() {
        return ftpFile.getType() == 1;
    }

    public Integer getParentId() {
        return parentId;
    }

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public String getOriginFileName() {
        return getFilename();
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    @Override
    public Boolean getIncludePage() {
        return includePage;
    }

    public void setIncludePage(Boolean includePage) {
        this.includePage = includePage;
    }
}
